package com.tedu.api.io;

import org.junit.Test;

import java.io.*;

/**IO流:传输数据
 *
 * File文件、文件夹类：无法读写数据
 * RandomAccessFile：读写按照字节，读写类不明确，靠构造器传入第二个参数确定
 *
 * Java  提出一套 标准 IO--读写单独提供类
 * ①方向区别：
 *   输入流--（读：针对文件，文件往程序中走，文件”相对于程序“）
 *   输出流--（写：针对文件，文件从程序中写出，文件”相对于程序“）
 *   输入流和输出流创建方式
 *   输入流  InputStream(抽象类）--顶级父类--读操作
 *   输出流 OutputStream(抽象类）--顶级父类--写操作
 *   以上二个流定义所有流具备读写操作，本身无法实例化
 *   ②按照功能进行区分：
 *
 *     低级流：专门搬运数据的流【叫做节点流】
 *
 *   FileInputStream   文件输入流：读操作 ---InputStream(抽象类）
 *   OutputStream      文件输出流：写操作 ---OutputStream(抽象类）
 *   功能：建立在程序与数据源之间的“管道”，所有数据传输通过低级流【节点流】
 *     高级流：不能独立存在的流【叫做处理流】
 *
 *           处理低级流的数据，并且可以被其他高级流所处理！
 *           理解：处理搬运数据的流【高级流种类比较多】
 *    ----------流：明确功能，创建对象：直接使用低级流--高级流创建
 *    项目：聊天室 Version1 IO流：针对字符串
 */
public class IOAPI {
    @Test
    public void testIO() throws IOException {
        /* RandomAccessFile a=new RandomAccessFile("fos.txt","rw");*/
        //创建标准IO流 方向划分 输入流输出流--功能：低级流【节点流】
        //需求：从项目中写出"fox.txt" 低级流：FileOutputStream
        //创建文件输出流---构造器("相对路径+文件全称")
        //抛出异常FileNotFoundEXception:没有文件的异常！
        //原因：路径或者文件名写错
        FileOutputStream fos = new FileOutputStream("fos.txt");
        /**
         * 输出一句话： 我们是祖国的花朵
         * write(字符串：字符数组) 返回值：void
         * 作用：写出一个文件内容:字节数组
         * 抛出异常 IOException IO流异常：写出失败
         */
        //String 字符串 方法：getBytes() 可以将字符串转换成字符数组char[]
        fos.write("我们是祖国的🔅".getBytes());
        //标准IO流必须关闭
        fos.close(); //释放流：避免内存占用

    }

    /**
     * FileOutputStream实现类---构造器使用方法
     * new FileOutputStream（String path，boolean append） 对
     * 通过第二个参数，确实数据是否追加，true代表追加，反之
     * new FileOutputStream（File file,boolean append);
     */
    @Test
    public void testIOA() throws IOException {
        //当前API 项目 下添加fos.txt--增加功能：追加效果


        //相对路径： ./fos.txt 直接写文件|不写./  都是当前目录下
        FileOutputStream fos =
                new FileOutputStream("fos.txt", true);
        String str = "jett~";
        fos.write(str.getBytes());
        //关闭流
        fos.close();


    }


    /**
     * 低级流
     * 文件输入流 FileInputStream
     * read(字节数组) 返回值：int
     * 作用：读取到一个字节
     */
    @Test
    public void testFileInputStream() throws IOException {
        //创建文件输入流--读入

        FileInputStream fis =
                new FileInputStream("fos.txt");
        //10KB 字节数组
        byte[] bytes = new byte[1024 * 10];
        //10KB 读 数据  【不想看到整数值】
        int len = fis.read(bytes);//len 返回 -1  文件读取完毕
        //String 字符串类--提供构造器方法：按照字节数组量，输出字符串
        //String 构造器 new String（字节数组，开始读取文件，结束读取文件）；
        //将【二进制值】整数 转换为 字符串
        String str = new String(bytes, 0, len);        //循环
        //一次性将给定的字节数组【10kb】写入到文件中，文件从0开始写入，到-1结束    写入完毕
        System.out.println("打印读：" + str);
        fis.close();


    }


    //需求：【标准IO】重构RandomAccessFile类--复制视频的方法
    @Test
    public void testWork() throws IOException {
        //1.创建输入流----读-----被复制文件【原文件】
        FileInputStream fis = new FileInputStream("复制文件.mp4");

        //2.创建输出流----写----复制品
        FileOutputStream fos = new FileOutputStream("标准IO.mp4");

        //3.用于保存读取到的字节量
        int len = -1;//①保存读取的数据值【可以被覆盖】  ②-1代表文件结尾

        //6.视频空间大，解决方案：10kb，字节数组：设定保存值10KB

        byte[] bytes = new byte[1024 * 10];

        //4.一直读取[10KB]文件,保存len中，如果结果不等于-1 ，一直读
        //(len=fis.read(bytes))!=-1
        long start = System.currentTimeMillis();//开始毫秒数
        while ((len = fis.read(bytes)) != -1) {

            //4.1将读取到的[10KB]文件写出
            fos.write(bytes, 0, len);
        }
        long end = System.currentTimeMillis(); //结束毫秒数
        System.out.println("测试传输时间：" + (end - start) + "毫秒数");

        //5.关闭流
        fis.close();
        fos.close();
    }


    /**
     * 高级流【处理流】：处理低级流以及其他高级流的一“种”流
     * 高级流不能独立存在，一定处理低级流
     * BufferedInputStream------高级流：快速读   缓冲输入流
     * BufferedInputStream------高级流：快速写   缓冲输出流
     * 以上两个高级流，内嵌一套按照实际传输数据大小，随机型改变的字节数组
     * 高级流写法：
     * BufferedInputStream bis=new BufferedInputStream(低级流引用);
     * 需求：视频    使用   IO流【低级流+高级流】-----实现视频快速复制
     */


    @Test
    public void testCV() throws IOException {
        //1.创建低级流：输入流:高级流、输出流：高级流
        FileInputStream fis = new FileInputStream("复制文件.mp4");

        BufferedInputStream bis = new BufferedInputStream(fis);

        FileOutputStream fos = new FileOutputStream("缓冲文件.mp4");

        BufferedOutputStream bos = new BufferedOutputStream(fos);

        //2.定义整数变量保存每次读取的字节量
        int len = -1;
        //3.将高级流：缓冲输入流--读取的数据--保存交给len，结果不等于-1
        //(len=bis.read())!=-1
        long start = System.currentTimeMillis();
        while ((len = bis.read()) != -1) {
            bos.write(len);
        }
        long end = System.currentTimeMillis();
        System.out.println("缓冲花费时间：" + (end - start));
        //4.关闭流
        fis.close();
        fos.close();



    }

    /**
     * 【高级流：处理流】
     * BufferedOutputStream缓冲输出流
     * 需求：写出一份文件：bos.txt，写出：一段文本内容
     * 所有的流，全部都不是正确的写法：IO流异常最多，采取捕获异常并做出处理
     */
    @Test
    public void testBOS() throws IOException {
        //创建低级流--写出bos.txt
        FileOutputStream fos = new FileOutputStream("bos.txt");
        //创建高级流--写出一段文本内容【缓冲区：字节数组】
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        String str = "50块钱再好看，也不如100块钱招人爱";

        //write(byte[] b) 作用：写出字节数组

        bos.write(str.getBytes());
        /**
         *        str数据量小  缓冲区大  强制送出【flush】 被动送出【closed】
         *        45人        车100    晚上9
         * flush()
         * 作用：可以强制将内部缓冲区数据一次性读写
         * 缺点：频繁使用flush降低读写
         */
        //bos.flush();
        bos.close();
        fos.close();
    }


    /**
     * 标准IO 必须与 异常存在  ---“传输数据”保障数据安全【处理异常】
     * 异常重点：1.认识代码中抛出异常【查API】、异常产生原因！
     * 2.异常处理--【快捷键：ctrl+alt+t】
     * 语法：try{
     * 怀疑可能有错误或者已经提示异常
     * }catch（小异常名字 e）{
     * e.print（）；打印异常信息--通过异常信息解决问题
     * }catch（中异常名字 e）{ catch可以捕获很多异常：小到大
     * <p>
     * }
     * finally{
     * 一定执行代码块
     * }
     * 考试题：  final与finally的区别？
     * final关键字：修饰 变量、方法、类-->变量成常量
     * 方法不能重写
     * 类不能被继承
     * finally关键字：异常处理机制中：捕获异常
     * finally在捕获异常中，可以写，也可以不写
     * 写finally关键字所有异常都不符合，一定会执行的语法块
     * 通常使用在IO流中：关闭流为主！关闭高级流-------->间接关闭低级流
     * <p>
     * 需求：BOS高级流---重新结合异常--->标准IO流
     */
    @Test
    public void testBOSIO() {
        //1.需要将流置空：置空--Ⅰ  覆盖，不用多次创建！Ⅱ  关闭流
        FileOutputStream fos = null; //低级流--搬运数据：写出
        BufferedOutputStream bos = null; //高级流--处理低级流：缓冲输出流
        //2.捕获异常操作:快捷键：ctrl+alt+t：怀疑IO流代码--捕获异常
        //2.1创建输出流
        try {
            fos = new FileOutputStream("BOS标准流写法.txt");
            bos = new BufferedOutputStream(fos);
            //3.高级流：缓冲输出流--功能：缓冲区【字节数组】写出数据
            bos.write("标准流写法".getBytes());
            //4.缓冲区：数据量比较小--采用flush（） 数据一次性写出

            bos.flush();

        } catch (FileNotFoundException e) { //文件不存在异常：路径写错，文件不存在

            e.printStackTrace();//打印异常信息

            System.out.println("[小]文件不存在异常");

        } catch (IOException e) { //IO流最大的异常：小异常不处理

            e.printStackTrace();
            System.out.println("[中]IO流异常");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("[大]异常");
        } finally {
            //一定执行代码块：IO流关闭--高级流--关闭高级流--低级流捎带关闭【捕获异常】
        }
        //                      低级流--关闭低级流【捕获异常】
          //5.先判断，高级流--是否为空【有高级流对象】---有高级流对象【不为null】
         //                                      没有高级流对象【不必关闭】
            if (bos!=null)
                try {
                    bos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

        }


    }





